package com.mjw.zstu.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.mjw.zstu.entity.Exhibition;
import com.mjw.zstu.entity.Orderlist;
import com.mjw.zstu.entity.Product;
import com.mjw.zstu.entity.Visitor;
import com.mjw.zstu.mapper.ExhibitonMapper;
import com.mjw.zstu.mapper.OrderMapper;
import com.mjw.zstu.mapper.ProductMapper;
import com.mjw.zstu.mapper.VisitorMapper;
import com.mjw.zstu.resultDto.JsonResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@EnableAsync
@EnableScheduling
public class RecommendService {

    private final static ConcurrentHashMap<String,Object> cache = new ConcurrentHashMap<>();

    private final static String CACHE_KEY = "re_list";

    private final static Exhibition updateEntity = new Exhibition();

    @Autowired
    VisitorMapper visitorMapper;

    @Autowired
    ExhibitonMapper exhibitonMapper;

    @Autowired
    ProductMapper productMapper;

    @Autowired
    OrderMapper orderMapper;

    @Async
    @Scheduled(cron = "0 0/30 * * * ?")
    void calculateRecommend(){
        long nowTimeStamp = System.currentTimeMillis();

        /** 获得在线的参会列表*/
        List<Exhibition> exhibitionList =exhibitonMapper.selectList(new QueryWrapper<Exhibition>()
                .eq("is_review",1)
                .eq("is_online",1)
                .eq("admin_recommend",0)
                .eq("is_banner_recommend",0)
                .le("start_time",nowTimeStamp)
                .ge("end_time",nowTimeStamp)
                .select("id"));

        Map<Integer,Boolean> exhibitionMap = null;
        Map<Integer,Long> visitorCountMap = null,orderCountMap = null;
        if (exhibitionList != null && exhibitionList.size() > 0){
            exhibitionMap = exhibitionList.stream()
                    .collect(Collectors.toMap(Exhibition::getId,e->Boolean.TRUE));

            List<Visitor> visitorList = visitorMapper.selectList(new QueryWrapper<Visitor>()
                    .in("exhibition_id",exhibitionMap.keySet()));

            /** 获得访问量计数(exhibition_id,count)*/
            visitorCountMap = visitorList
                    .stream()
                    .collect(Collectors.groupingBy(Visitor::getExhibition_id,Collectors.counting()));

            List<Product> productList = productMapper.selectList(new QueryWrapper<Product>()
                    .in("exhibition_id",exhibitionMap.keySet())
                    .select("exhibition_id","id"));

            if (productList != null && productList.size() > 0){
                Map<Integer,Integer> productIdExhibitionIdMap = productList
                        .stream()
                        .collect(Collectors.toMap(Product::getId,Product::getExhibition_id));

                List<Orderlist> orderList = orderMapper.selectList(new QueryWrapper<Orderlist>()
                        .in("product_id",productIdExhibitionIdMap.keySet()));

                if (orderList != null && orderList.size() > 0){
                    /** 获得购买量计数(exhibition_id,count)*/
                    orderCountMap = orderList
                            .stream()
                            .collect(Collectors.groupingBy(e->productIdExhibitionIdMap
                                    .get(e.getProduct_id()),Collectors.counting()));
                }
            }
        }
        Map<Integer,HotCacheEntry> hotCacheMap = null;
        HashSet<Integer> keySet = new HashSet<>(exhibitionMap != null? exhibitionMap.keySet():Collections.emptySet());
        if (cache.containsKey("score_cache")){
            hotCacheMap = (Map<Integer, HotCacheEntry>) cache.get("score_cache");
            keySet.addAll(hotCacheMap.keySet());
        }else {
            hotCacheMap = new HashMap<>();
        }
        if (visitorCountMap != null){
            keySet.addAll(visitorCountMap.keySet());
        }
        if (orderCountMap != null){
            keySet.addAll(orderCountMap.keySet());
        }

        for (Integer key : keySet){
            HotCacheEntry cacheEntry = hotCacheMap.get(key);
            if (cacheEntry == null){
                cacheEntry = new HotCacheEntry(key,nowTimeStamp,100);
            }else {
                /** 热度小于20 ，并且不在已经 结束或下线的，移除缓存，不再参与热度计算 */
                if (cacheEntry.getScore() < 20 &&
                        exhibitionMap!= null &&
                        !exhibitionMap.containsKey(key)){
                    hotCacheMap.remove(key);
                }
            }

            /** 权重 参会人数 5 ， 下单量10, 固定数 100*/
            float visitScore = visitorCountMap != null ? visitorCountMap.getOrDefault(key, 0L):0;
            float orderScore = orderCountMap != null ? orderCountMap.getOrDefault(key,0L):0;
            double score  = (100 + 5* visitScore + 10 * orderScore)/
                    Math.exp((nowTimeStamp - cacheEntry.getStartTime()) * 2.0);
            cacheEntry.setScore(score);
            hotCacheMap.put(key,cacheEntry);
        }
        cache.put("score_cache",hotCacheMap);
        List<Map.Entry<Integer,HotCacheEntry>> list = new ArrayList<>(hotCacheMap.entrySet());
        list.sort((e1, e2) -> (int) (e2.getValue().getScore() - e1.getValue().getScore()));
        if (list.size() > 0) {
            /** 前20条 */
            if (list.size() > 5) {

                list = list.subList(0, 5);
            }
            List<Integer> ids = list
                    .stream()
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList());
            cache.put(CACHE_KEY,
                    new JsonResult<>(200, "请求成功！", ids));


            /** 取消旧的推荐 */
            updateEntity.setIs_recommend(0);
            exhibitonMapper.update(updateEntity,new UpdateWrapper<>());
            updateEntity.setIs_recommend(1);

            /** 新增推荐 */
            exhibitonMapper.update(updateEntity,new UpdateWrapper<Exhibition>().in("id",ids));
        }


    }

    /**
     * 获得推荐列表
     *
     * @return {@link JsonResult}
     */
    public JsonResult<?> getRecommendList(){
        if (!cache.containsKey(CACHE_KEY)){
            this.calculateRecommend();
        }
        return (JsonResult<?>) cache.get(CACHE_KEY);
    }
}

/**
 * 热度缓存
 *
 * @date 2021/05/02
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
class HotCacheEntry {
    /**
     * 展览Id
     */
    Integer exhibitionId;

    /**
     * 开始时间
     */
    long startTime;

    /**
     * 分数
     */
    double score;
}